use std::sync::{Arc, Mutex};

use model_graph_types::{
    container::workflow::{
        WorkflowBlock, WorkflowBlockBody, WorkflowBlockBuilder, WorkflowBlockExecuteResult,
        WorkflowBlockExecuteResultBuilder, WorkflowBlockIfElse, WorkflowBlockIfElseBuilder,
        WorkflowBlockReturn, WorkflowCondition, WorkflowGraphNodeIfElse,
    },
    modeling::{ComparsionOperator, LogicalOperator, Value},
};

use crate::workflow::context::{Getter, WorkflowContext};

pub async fn compile(id: &String, node: &WorkflowGraphNodeIfElse) -> anyhow::Result<WorkflowBlock> {
    return Ok(WorkflowBlockBuilder::default()
        .id(id)
        .body(WorkflowBlockBody::IfElse(
            WorkflowBlockIfElseBuilder::default()
                .cases(node.cases.clone())
                .build()?,
        ))
        .build()?);
}

pub async fn execute(
    block: &WorkflowBlockIfElse,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<WorkflowBlockExecuteResult> {
    //执行cases
    let cases = &block.cases;

    if cases.len() == 0 {
        return Ok(WorkflowBlockExecuteResult::succeed());
    }
    //
    let mut builder = WorkflowBlockExecuteResultBuilder::default();

    for case in cases {
        //
        let case_id = &case.case_id;
        let operator = &case.logical_operator;

        let case_result = workflow_conditions_result(operator, &case.conditions, context.clone())?;
        if case_result {
            //
            builder.source_handle(case_id);
            return Ok(builder.build()?);
        }
    }
    Ok(builder.build()?)
}

pub fn workflow_condition_result(
    condition: &WorkflowCondition,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<bool> {
    //获取到值
    if let Ok(value) = context.get_from_node(
        &condition.variable_selector.1,
        &condition.variable_selector.0,
    ) {
        // condition.var_type,
        return _string_compare(&value, &condition.comparsion_operator, &condition.value);
    }

    Ok(true)
}

pub fn workflow_conditions_result(
    operator: &LogicalOperator,
    conditions: &Vec<WorkflowCondition>,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<bool> {
    //获取到值
    let mut result = {
        match operator {
            LogicalOperator::And => true,
            LogicalOperator::Or => false,
            _ => false,
        }
    };
    for condition in conditions {
        if let Ok(value) = context.get_from_node(
            &condition.variable_selector.1,
            &condition.variable_selector.0,
        ) {
            let r = _string_compare(&value, &condition.comparsion_operator, &condition.value)?;

            result = {
                match operator {
                    LogicalOperator::And => result && r,
                    LogicalOperator::Or => result || r,
                    _ => return Ok(false),
                }
            };
            match operator {
                LogicalOperator::And => {
                    if !result {
                        return Ok(false);
                    }
                }
                LogicalOperator::Or => {
                    if result {
                        return Ok(true);
                    }
                }
                _ => return Ok(false),
            }
        }
    }

    Ok(result)
}

fn _string_compare(
    left: &Value,
    operator: &ComparsionOperator,
    right: &String,
) -> anyhow::Result<bool> {
    //
    let left_str: String = left.try_into()?;
    let result = match operator {
        ComparsionOperator::Contains => left_str.contains(right),
        ComparsionOperator::NotContains => !left_str.contains(right),
        ComparsionOperator::StartWith => left_str.starts_with(right),
        ComparsionOperator::EndWith => left_str.ends_with(right),
        ComparsionOperator::Is => left_str.eq(right),
        ComparsionOperator::IsNot => !left_str.eq(right),
        ComparsionOperator::Empty => left_str.is_empty(),
        ComparsionOperator::NotEmpty => !left_str.is_empty(),
        ComparsionOperator::Equal => left_str.eq(right),
        ComparsionOperator::NotEqual => !left_str.eq(right),
        _ => false,
    };
    Ok(result)
}
